1.在table中的末尾插入一个值有两种方式,我们来看有什么不同
a = {}
table.insert(a, 1)
a[#a + 1] = 1
2.将代码解析成token
285(TK_NAME) -> 61(=) -> 123({) -> 125(})
285(TK_NAME) -> 46(.) -> 285(TK_NAME) -> 40(() -> 285(TK_NAME) -> 44(,) ->284(TK_NUMBER) -> 41())
285(TK_NAME) -> 91([) -> 35(#)-> 285(TK_NAME)-> 43(+)-> 284(TK_NUMBER)-> 93(])-> 61(=)-> 284(TK_NUMBER)
3.实际执行的指令
OP_NEWTABLE
OP_SETGLOBAL
OP_GETGLOBAL
OP_GETTABLE
OP_GETGLOBAL
OP_LOADK
OP_CALL
OP_GETGLOBAL
OP_GETGLOBAL
OP_LEN
OP_ADD
OP_SETTABLE
OP_RETURN
4.指令分析
指令分四部分看:
第一部分OP_NEWTABLE、OP_SETGLOBAL 执行了 a = {}的操作
第二部分 OP_GETGLOBAL、OP_GETTABLE、OP_GETGLOBAL、OP_LOADK、OP_CALL 执行了 table.insert(a, 1)的操作
case OP_GETGLOBAL:
{
TValue g;
TValue *rb = KBx(i); // 取出值 "table" (char *)(&(rb)->value.gc->ts + 1)
sethvalue(L, &g, cl->env);
gafq_assert(ttisstring(rb));
Protect(gafqV_gettable(L, &g, rb, ra)); // 取出 g(是一个table) 中rb的值 key 为 "table"得内容 放入 ra寄存器中
continue;
}
case OP_GETTABLE:
{
Protect(gafqV_gettable(L, RB(i), RKC(i), ra)); // 上一步我们取出的table, 这时候的RB(i) 就是上个指令存取ra的值,
// 这时候 我们从RKC(i)的值为insert, 那么这里就是 容table中取出insert 放入ra寄存器中
continue;
}
case OP_GETGLOBAL:
{
TValue g;
TValue *rb = KBx(i); // 取出值 "a" (char *)(&(rb)->value.gc->ts + 1)
sethvalue(L, &g, cl->env);
gafq_assert(ttisstring(rb));
Protect(gafqV_gettable(L, &g, rb, ra)); // 从g中取出值是a的数据放入ra中
continue;
}
case OP_LOADK:
{
setobj2s(L, ra, KBx(i)); // 从常量中KBx(i) 取出数值 1 放入ra中
continue;
}
case OP_CALL:
{
//函数调用
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0)
L->top = ra + b;
L->savedpc = pc;
switch (gafqD_precall(L, ra, nresults))
{
case PCRC:
{
if (nresults >= 0)
L->top = L->ci->top;
base = L->base;
continue;
}
default:
{
return; /* yield */
}
}
}
// 执行gtablib.c中的tinsert方法
static int tinsert(gafq_State *L)
{
int e = aux_getn(L, 1) + 1; /* first empty element */
int pos; /* where to insert new element */
switch (gafq_gettop(L))
{
case 2:
{
pos = e;
break;
}
}
gafqL_setn(L, 1, e); /* new size */
gafq_rawseti(L, 1, pos); /* t[pos] = v */
return 0;
}
GAFQ_API void gafq_rawseti(gafq_State *L, int idx, int n)
{
'''
setobj2t(L, gafqH_setnum(L, hvalue(o), n), L->top - 1);
'''
}
// table获取值
TValue *gafqH_setnum(gafq_State *L, Table *t, int key)
{
TValue k;
setnvalue(&k, cast_num(key));
return newkey(L, t, &k);
}
第三部分 OP_GETGLOBAL、OP_GETGLOBAL、OP_LEN、OP_ADD、OP_SETTABLE 执行了 a[#a + 1] = 1 的操作
case OP_GETGLOBAL:
{
//获取全局
TValue g;
TValue *rb = KBx(i); // 取出值a
sethvalue(L, &g, cl->env);
gafq_assert(ttisstring(rb));
Protect(gafqV_gettable(L, &g, rb, ra)); // 把g中的a的表 放入寄存器ra中
continue;
}
case OP_GETGLOBAL:
{
//获取全局
TValue g;
TValue *rb = KBx(i); // 取出值a 也就是 a[#a + 1] = 2 中的第二个a
sethvalue(L, &g, cl->env);
gafq_assert(ttisstring(rb));
Protect(gafqV_gettable(L, &g, rb, ra)); // 把g中的a的表 放入寄存器ra中
continue;
}
case OP_LEN:
{
// 获取长度
const TValue *rb = RB(i);
switch (ttype(rb))
{
case GAFQ_TTABLE:
{
setnvalue(ra, cast_num(gafqH_getn(hvalue(rb)))); // 这里大概是一个二分取长度 与第二部分中的aux_getn是一样的
break;
}
}
continue;
}
case OP_ADD:
{
arith_op(gafqi_numadd, TM_ADD);
continue;
}
case OP_SETTABLE:
{
Protect(gafqV_settable(L, ra, RKB(i), RKC(i))); // 设置值
continue;
}
void gafqV_settable(gafq_State *L, const TValue *t, TValue *key, StkId val)
{
TValue *oldval = gafqH_set(L, h, key); /* do a primitive set */
}
TValue *gafqH_set(gafq_State *L, Table *t, const TValue *key)
{
return newkey(L, t, key);
}
第四部分 OP_RETURN 返回结果
5. 结论
从4的分析看来,两者的差别 是最后创建newkey的时候的key是怎么得来的
table.insert(a, 1) 中 是调用了gtablib.c中的tinsert的c函数算来的
a[#a + 1] = 1则是使用OP_ADD计算得来的, 两者在计算当前a的长度 是使用了相同的方法
前者只用c函数 计算 长度 设置 newkey
后者 则使用两个指令 OP_ADD、OP_SETTABLE 完成相同的操作